#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <execinfo.h>
#include <libconfig.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#include "htmacro.h"
#include "htcfg.h"
#include "htssl.h"
#include "httun.h"
#include "htthr.h"
#include "htwork.h"
#include "htauth.h"
#include "htlist.h"
#include "httcp.h"

// 运行状态
int gRun = 1;

// 段错误时输出堆栈
static void segv_handler(int signum)
{
    size_t i = 0;
    size_t size = 0;
    void *array[10];
    char **strs;

    size = backtrace(array, 10);
    strs = backtrace_symbols(array, size);

    for(i = 0; i < size; i++) {
        printf("%ld:%s", i, strs[i]);
    }

    signal(signum, SIG_DFL);
    free(strs);
}

// 运行状态标志配置
static void int_handler(int signum)
{
    printf("htssl: signal %d\n", signum);
    gRun = 0;
    signal(signum, SIG_DFL);
}

// 单例检测
static int single_case(const char *path)
{
    int fd = 0;
    struct flock lock = {0};    // 锁

    // 创建文件
    fd = open((char*)path, O_WRONLY | O_CREAT, 0666);
    if(fd < 0) {
        return -1;
    }

    // 获取锁
    bzero(&lock, sizeof(lock));
    if(fcntl(fd, F_GETLK, &lock) < 0) {
        return -1;
    }

    // 锁定文件
    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    if(fcntl(fd, F_SETLK, &lock) < 0) {
        return -1;
    }

    return fd;
}

// 配置隧道模式工作线程
static int htssl_tunnel_start(htssl_info_t *pInfo, struct htthr *pTun, struct htthr *pSSL)
{
    int rv = 0;

    // 建立国密SSL隧道
    rv = htssl_create(pInfo->pcfg, pInfo, pInfo->pcfg->gip, pInfo->pcfg->gport);
    if (rv < 0) {
        printf("国密SSL隧道建立失败.\n");
        return -1;
    }
    printf("隧道模式:国密SSL隧道建立完成.\n");

    // 创建虚拟网卡
    rv = httun_create(pInfo->pcfg, pInfo);
    if (rv < 0) {
        printf("虚拟网卡创建失败.\n");
        goto ERROR;
    }
    printf("虚拟网卡创建成功.\n");

    // ACL信息链表初始化
    rv = list_init(HT_LIST_MAX_LEN);
    if (rv < 0) {
        printf("ACL信息链表初始化失败.\n");
        goto ERROR;
    }

    // 终端认证
    rv = htauth_process(pInfo->pcfg, pInfo);
    if (rv < 0) {
        printf("终端认证失败.\n");
        goto ERROR;
    }
    printf("终端认证成功.\n");

    // SSL隧道数据收线程
    pSSL->bthrun = TRUE;
    pSSL->arg = pInfo;
    pSSL->run = htwork_ssl;

    // 虚拟网卡数据收线程
    pTun->bthrun = TRUE;
    pTun->arg = pInfo;
    pTun->run = htwork_tun;

    return 0;
ERROR:
    // 释放资源
    list_destory(free);
    httun_close(pInfo);
    htssl_final(pInfo);

    return -1;
}

// 终止隧道模式工作线程
static void htssl_tunnel_stop(htssl_info_t *pInfo)
{
    // 释放资源
    list_destory(free);
    httun_close(pInfo);
    htssl_final(pInfo);

    return;
}


// 配置代理模式工作线程
static int htssl_proxy_start(htssl_info_t *pInfo, struct htthr *pLocal, struct htthr *pSSL)
{
    int rv = 0;
    int fd = 0;

    // 建立国密SSL隧道
    rv = htssl_create(pInfo->pcfg, pInfo, pInfo->pcfg->sip, pInfo->pcfg->sport);
    if (rv < 0) {
        printf("国密SSL隧道建立失败.\n");
        return -1;
    }
    printf("代理模式:国密SSL隧道建立完成.\n");

    // 监听本地端口
    fd = httcp_init(pInfo->pcfg->lip, pInfo->pcfg->lport);
    if (fd < 0) {
        printf("监听本地端口失败.\n");
        goto ERROR;
    }
    printf("本地服务启动成功.\n");

    pInfo->fdTun = fd;
    // 本地代理数据收线程
    pLocal->bthrun = TRUE;
    pLocal->arg = pInfo;
    pLocal->run = htwork_local;

    // SSL隧道数据收线程
    pSSL->bthrun = TRUE;
    pSSL->arg = pInfo;
    pSSL->run = htwork_server;

    return 0;
ERROR:
    // 释放资源
    htssl_final(pInfo);

    return -1;
}

// 终止隧道模式工作线程
static void htssl_proxy_stop(htssl_info_t *pInfo)
{
    httcp_deinit(pInfo->fdLocal);
    // 释放资源
    htssl_final(pInfo);

    return;
}

int main(int argc, char *argv[])
{
    int rv = 0;
    ht_cfg_t cfg = {0};             // 配置参数
    htssl_info_t tunInfo = {0};     // 隧道模式SSL连接信息
    struct htthr recvTun = {0};     // 接收隧道数据线程
    struct htthr recvSSL = {0};     // 接收SSL隧道数据线程
    htssl_info_t sslInfo = {0};     // 代理模式SSL连接信息
    struct htthr recvLocal = {0};   // 接收本地代理数据线程
    struct htthr recvServer = {0};  // 接收SSL代理数据线程
    
    // 重置信号量
    signal(SIGSEGV, segv_handler);
    signal(SIGINT, int_handler);    // ctrl + c
    signal(SIGTERM, int_handler);   // kill
    signal(SIGKILL, int_handler);   // kill -9

    // 单列检测
    rv = single_case(LOCK_FILE);
    if(rv < 0) {
        printf("已经有一个实例在运行.\n");
        goto ERROR;
    }

    // 命令行参数解析
    rv = htcfg_load(&cfg, argc, argv);
    if (rv < 0) {
        printf("运行参数解析失败.\n");
        goto ERROR;
    }

    // 配置文件解析
    rv = htcfg_parse(&cfg, cfg.cfgfile);
    if (rv < 0) {
        printf("配置文件解析失败 %s.\n", cfg.cfgfile);
        goto ERROR;
    }
    printf("配置文件解析完成.\n");

    // 工作模式
    switch (cfg.mode)
    {
    case HT_MODE_TUNNEL:    // 隧道模式
        // 建立国密SSL隧道
        tunInfo.pcfg = &cfg;
        rv = htssl_tunnel_start(&tunInfo, &recvTun, &recvSSL);
        if (rv < 0) {
            goto ERROR;
        }
        break;
    case HT_MODE_PROXYD:    // 代理模式
        // 建立国密SSL隧道
        sslInfo.pcfg = &cfg;
        rv = htssl_proxy_start(&sslInfo, &recvLocal, &recvServer);
        if (rv < 0) {
            goto ERROR;
        }
        break;
    case HT_MODE_COMPRE:    // 综合模式
        // 建立隧道模式国密SSL隧道
        tunInfo.pcfg = &cfg;
        rv = htssl_tunnel_start(&tunInfo, &recvTun, &recvSSL);
        if (rv < 0) {
            goto ERROR;
        }
        // 建立代理模式国密SSL隧道
        sslInfo.pcfg = &cfg;
        rv = htssl_proxy_start(&sslInfo, &recvLocal, &recvServer);
        if (rv < 0) {
            goto ERROR;
        }
        break;
    default:
        goto ERROR;
        break;
    }

    // 更新状态
    htcfg_upstate(&cfg, 1);
    daemon(0, 0);

    // 启动工作线程
    thr_start(&recvSSL);
    thr_start(&recvTun);
    thr_start(&recvLocal);
    thr_start(&recvServer);

    while (gRun);

    // 线程退出
    thr_exit(&recvTun);
    thr_exit(&recvSSL);
    thr_exit(&recvLocal);
    thr_exit(&recvServer);
    // 线程取消
    thr_cancel(&recvTun);
    thr_cancel(&recvSSL);
    thr_cancel(&recvLocal);
    thr_cancel(&recvServer);
    // 等待线程退出
    thr_wait(&recvTun);
    thr_wait(&recvSSL);
    thr_wait(&recvLocal);
    thr_wait(&recvServer);
ERROR:
    // 资源回收
    htssl_tunnel_stop(&tunInfo);
    htssl_proxy_stop(&sslInfo);
    // 更新状态
    htcfg_upstate(&cfg, 0);
    // 资源回收
    htcfg_final(&cfg);

    return 0;
}
